package com.carson.data.zuul.filter;

import com.carson.data.zuul.EncryptEntity;
import com.carson.data.zuul.config.DataFilterConfig;
import com.carson.data.zuul.util.AESUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.InputStream;
import java.nio.charset.Charset;

@Component
public class PreEncryptFilter extends ZuulFilter {
    private static Logger log = LoggerFactory.getLogger(PreEncryptFilter.class);
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private DataFilterConfig dataFilterConfig;

    /**
     * pre：路由之前
     * routing：路由之时
     * post： 路由之后
     * error：发送错误调用
     *
     * @return
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * filterOrder：过滤的顺序
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * shouldFilter：这里可以写逻辑判断，是否要过滤，本文true,永远过滤
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        // 获取到request
        RequestContext ctx = RequestContext.getCurrentContext();
        if (Boolean.FALSE.equals(ctx.get("isSuccess"))) {
            return false;
        }

        HttpServletRequest request = ctx.getRequest();
        if (!dataFilterConfig.getEncryptExcludeUri().contains(request.getRequestURI())) {
            return true;
        }
        return false;
    }

    /**
     * run：过滤器的具体逻辑。
     * 要把请求参数进行验签（解密）之后传给后续的微服务，首先获取到request，但是在request中只有getParameter()而没有setParameter()方法
     * 所以直接修改url参数不可行，另外在reqeust中虽然可以使用setAttribute(),但是可能由于作用域（request）的不同，一台服务器中才能getAttribute
     * 在这里设置的attribute在后续的微服务中是获取不到的，因此必须考虑另外的方式：即获取请求的输入流，并重写，即重写json参数，
     * ctx.setRequest(new HttpServletRequestWrapper(request) {})，这种方式可重新构造上下文中的request
     *
     * @return
     */
    @Override
    public Object run() {

        // 获取到request
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        // 获取请求参数name
        String name = "";
        try {

            // 请求方法
            String method = request.getMethod();
            log.info(String.format("%s >>> %s", method, request.getRequestURL().toString()));
            // 获取请求的输入流
            InputStream in = request.getInputStream();
            String body = StreamUtils.copyToString(in, Charset.forName("UTF-8"));
            // 如果body为空初始化为空json
            if (StringUtils.isBlank(body)) {
                body = "{}";
            }
            log.info("body" + body);

            EncryptEntity encryptEntity = objectMapper.readValue(body, EncryptEntity.class);

            if ("POST".equals(method) || "PUT".equals(method)) {
                String token = request.getHeader("token");
                //token前10位+6位key
                String key = StringUtils.right(token, 10) + AESUtil.KEY;
                String newBody = AESUtil.aesDecrypt(encryptEntity.getData(), key);
                log.info("newBody" + newBody);
                final byte[] reqBodyBytes = newBody.getBytes();

                // 重写上下文的HttpServletRequestWrapper
                ctx.setRequest(new HttpServletRequestWrapper(request) {
                    @Override
                    public ServletInputStream getInputStream() {
                        return new ServletInputStreamWrapper(reqBodyBytes);
                    }

                    @Override
                    public int getContentLength() {
                        return reqBodyBytes.length;
                    }

                    @Override
                    public long getContentLengthLong() {
                        return reqBodyBytes.length;
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
